# -*- encoding: utf-8 -*-

# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

import gobject

from common import *
from mediainfo import MediaInfo

class Playback(gobject.GObject):
    """
    wrapper class for xmms_playback_ functions
    Stores information about the current song, too
    """

    def __init__(self, client):
        """
        client is an MClient class
        """
        self.__gobject_init__()
        self.client = client
        self.status_string = _("Not connected")
        self.status = -1
        self.volume = {'right' : 0, 'left': 0}
        self.current_info = MediaInfo()
        self.playtime = 0

        self.client.connect('e_playback_status', self.status_changed)

        self.start_timers()

    def start_timers(self):
        """
        start timer for emitting playtime signal every second,
        Somehow this still eats a little bit of processor - I think especially
        updating the statusbar
        """
        self.timer = gobject.timeout_add_seconds(1, self.timer_action)
    
    def timer_action(self):
        """
        timer function started by start_timers
        """
        self.client.emit("e_playback_playtime", self.playtime)
        return True

    @check_fast(True)
    @check_wait()
    def current_id(self, cb = None):
        """
        get the current id from the server.
        In most cases current_info['id'] is enough
        """
        if cb:
            res = self.client.xmms.playback_current_id(cb)
        else:
            res = self.client.xmms.playback_current_id(self.__current_id)
        return res

    @check_result_error(False)
    def __current_id(self, result):
        """
        default callback for setting the current information
        """
        self.client.medialib.get_info(result.value(), cb = self.__current_info)
        return False

    @check_result_error(False)
    def __current_info(self, nfo):
        self.current_info = nfo
        return False

    def status_changed(self, widget = None, val = -1):
        """
        set the status flag and set playtime to 0, if stopped
        """
        try:
            del self._volume_changed
        except:
            pass
        status = -1
        status_string = ""
        if val == xmmsclient.PLAYBACK_STATUS_PAUSE:
            status = PB_PAUSE
            status_string = _("Paused")
        elif val == xmmsclient.PLAYBACK_STATUS_PLAY:
            status = PB_PLAY
            status_string = _("Playing")
        elif val == xmmsclient.PLAYBACK_STATUS_STOP:
            status = PB_STOP
            status_string = _("Stopped")
            self.playtime = 0
        self.status = status
        self.status_string = status_string
        self.timer_action()


    @check_cb_None()
    @check_wait(False)
    @check_fast(True)
    def pause(self, cb = None):
        """
        pause the playback
        """
        return self.client.xmms.playback_pause(cb)

    def toggle(self, cb = None, wait = False, fast = True):
        """
        toggle playback
        """
        if self.status in (PB_PAUSE, PB_STOP):
            self.start(cb = cb, wait = wait, fast = fast)
        elif self.status == PB_PLAY:
            self.pause(cb = cb, wait = wait, fast = fast)

    @check_wait()
    @check_fast(True)
    def playtime_get(self, cb= None):
        """
        get the exact playtime
        client.playback.playtime should be enough in most cases... it's as precise as
        the server sends us the playback_playtime_signal.
        """
        if cb:
            res = self.client.xmms.playback_playtime(cb)
        else:
            res = self.client.xmms.playback_playtime(lambda result: self._playtime_cb(result, emit=True))
        return res

    @check_result_error(True)
    def _playtime_cb(self, result, emit = False):
        self.playtime = result.value()
        if emit:
            self.client.emit("e_playback_playtime", self.playtime)
        return False

    def prev(self, wait = False, fast = True):
        """
        play the previous song in the playlist
        """
        self.client.playlist.set_next_rel(-1, wait=wait, fast=fast)
        self.client.playback.play(wait=wait, fast=fast)
        self.tickle(wait=wait, fast=fast)

    def next(self, wait = False, fast = True):
        """
        play the nex song in the playlist
        """
        self.client.playlist.set_next_rel(1, wait=wait, fast=fast)
        self.client.playback.play(wait=wait, fast=fast)
        self.tickle(wait=wait, fast=fast)

    @check_wait(False)
    @check_fast(True)
    def seek_ms(self, ms):
        """
        seek to milliseconds in the song
        """
        cb = self.__playtime_up
        if self.status == PB_STOP:
            self.playtime = 0
            self.__playtime_up(None)
            return None
        elif self.status == PB_PAUSE:
            cb = lambda result: self.__playtime_up(result, pause=True)
            self.start()
        self.playtime = ms
        return self.client.xmms.playback_seek_ms(ms, cb=cb)

    @check_wait(False)
    @check_fast(True)
    def seek_ms_rel(self, ms, cb = None):
        """
        seek relatively to current position in the song
        """
        if cb == None:
            cb = self.__playtime_up
        if self.status == PB_STOP:
            self.playtime = 0
            self.__playtime_up(None)
            return None
        elif sely.status == PB_PAUSE:
            cb = lambda result: self.__playtime_up(result, pause=True)
            self.start()
        self.playtime += ms
        return self.client.xmms.playback_seek_ms_rel(ms, cb)

    def __playtime_up(self, res, pause = False):
        self.timer_action()
        if pause:
            self.pause(wait=False, fast=True)
    
    @check_cb_None()
    @check_wait()
    @check_fast(True)
    def start(self, cb = None):
        """
        start the playback
        """
        try:
            if self._volume_changed:
                self._block_volume = True
                cb = self._unblock_volume
        except:
            pass
        return self.client.xmms.playback_start(cb)

    @check_result_error(True)
    def _unblock_volume(self, result = None):
        del self._block_volume

        #during stop can't set the volume, so this is kinda workaround
        for channel in self.volume.iterkeys():
            self.client.xmms.playback_volume_set(channel, self.volume[channel])
        return False

    play = start
    
    @check_cb_None()
    @check_fast(True)
    @check_wait(False)
    def status(self, cb = None):
        """
        update the status,
        """
        return self.client.xmms.playback_status(cb)

    @check_cb_None()
    @check_fast(True)
    @check_wait(False)
    def stop(self, cb = None):
        """
        stop the playback
        """
        return self.client.xmms.playback_stop(cb)

    @check_cb_None()
    @check_fast(True)
    @check_wait()
    def tickle(self, cb = None):
        """
        stop processing the current song, start processing the song set by the 
        playlists set_next
        """
        return self.client.xmms.playback_tickle(cb)
    
    @check_fast(True)
    @check_wait()
    def volume_get(self, cb = None):
        """
        update volume manually
        """
        if cb == None:
            cb = self._volume_get_cb
        return self.client.xmms.playback_volume_get(cb)

    @check_result_error(False)
    def _volume_get_cb(self, result):
        try:
            if self._block_volume: #if we are in stop mode
                return False
        except AttributeError:
            pass
        vol = result.value()
        if vol != None:
            self.volume = result.value()
        self.client.emit("e_playback_volume_changed", self.volume)
        return False

    def volume_set(self, volume, channel = None, cb = None, fast = True, wait = False):
        """
        set the volume. If the playback is stopped xmms2 won't allow setting
        volume, se we store it locally, and the volume is set right after the start
        of the playback
        """
        volume = max(0.0, min(100.0, volume))
        if self.status == PB_STOP:
            if channel != None:
                try:
                    self.volume[channel] = volume
                except KeyError:
                    pass
            else:
                for channel in self.volume.keys():
                    self.volume[channel] = volume
            self._volume_changed = True
            self.client.emit("e_playback_volume_changed", self.volume)
        else:
            if channel != None:
                self.__x_volume_set(volume, channel, cb = cb, fast = fast, wait = wait)
            else:
                for channel in self.volume.keys():
                    self.__x_volume_set(volume, channel, cb = cb, fast = fast, wait = wait)

    @check_cb_None()
    @check_fast(True)
    @check_wait(False)
    def __x_volume_set(self, volume, channel, cb = None):
        return self.client.xmms.playback_volume_set(channel, int(volume), cb)

    def volume_set_relative(self, diff = 5, channel = None, wait = False, fast = True):
        """
        set the volume relatively to it's current state
        """
        for channel in self.volume.keys():
            self.volume[channel] = diff+self.volume[channel]
            self.volume_set(self.volume[channel], channel, wait = wait, fast = fast)

    def _set_current_id(self, id):
        """
        set the current id - this will update the current_info
        """
        self.client.medialib.get_info(id, cb = self.__set_current_id)
    
    def __set_current_id(self, info):
        if info == {}:
            return
        try:
            if info['id'] == self.current_info['id']:
                self.current_info = info
                self.client.emit('e_medialib_entry_changed', info['id'])
                return False
        except KeyError:
            pass
        self.current_info = info
        self.client.emit('e_playback_current_id', info.get(u'id'))


